package javacommon.base;

import static cn.org.rapid_framework.util.SqlRemoveUtils.removeFetchKeyword;
import static cn.org.rapid_framework.util.SqlRemoveUtils.removeOrders;
import static cn.org.rapid_framework.util.SqlRemoveUtils.removeSelect;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javacommon.xsqlbuilder.SafeSqlProcesser;
import javacommon.xsqlbuilder.SafeSqlProcesserFactory;
import javacommon.xsqlbuilder.XsqlBuilder;
import javacommon.xsqlbuilder.XsqlBuilder.XsqlFilterResult;
import javacommon.xsqlbuilder.safesql.DirectReturnSafeSqlProcesser;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.Expression;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.dialect.Dialect;
import org.hibernate.impl.SessionFactoryImpl;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

import cn.org.rapid_framework.page.Page;
import cn.org.rapid_framework.page.PageRequest;

/**
 * @author badqiu
 */
@SuppressWarnings({"deprecation","rawtypes","unchecked","unused"})
public abstract class BaseHibernateDao<E,PK extends Serializable> extends HibernateDaoSupport implements EntityDao<E,PK>{
	/**
	 * Logger for subclass
	 */
	private final static Logger log = Logger.getLogger(BaseHibernateDao.class);
	
	public long queryForLong(final String queryString) {
		return queryForLong(queryString,new Object[]{});
	}
	
	public long queryForLong(final String queryString,Object value) {
		return queryForLong(queryString,new Object[]{value});
	}
	
	public long queryForLong(final String queryString,Object[] values) {
		return DataAccessUtils.longResult(getHibernateTemplate().find(queryString, values));
	}
	
	/**
	 * 得到全部数据,但执行分页
	 * @param pageRequest
	 * @return
	 */
	public Page findAll(final PageRequest pageRequest) {
		return (Page)getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				
				StringBuffer queryString = new StringBuffer(" FROM ").append(getEntityClass().getSimpleName());
				String countQueryString = "SELECT count(*) " + queryString.toString();
				if(StringUtils.hasText(pageRequest.getSortColumns())) {
					queryString.append(" ORDER BY "+pageRequest.getSortColumns());
				}
				
				Query query = session.createQuery(queryString.toString());
				Query countQuery = session.createQuery(countQueryString);
				return PageQueryUtils.executeQueryForPage(pageRequest, query, countQuery);
			}
		});
	}
	
	public Page pageQuery(final String hql,final PageRequest pageRequest) {
		final String countQuery = "select count(*) " + removeSelect(removeFetchKeyword((hql)));
		return pageQuery(hql,countQuery,pageRequest);
	}
	
	public Page pageQuery(final StringBuffer hql,final PageRequest pageRequest) {
		final String countQuery = "select count(*) " + removeSelect(removeFetchKeyword((hql.toString())));
		return pageQuery(hql.toString(),countQuery,pageRequest);
	}
	
	public Page pageQuery(final String hql,String countQuery,final PageRequest pageRequest) {
		Map otherFilters = new HashMap(1);
		otherFilters.put("sortColumns", pageRequest.getSortColumns());
		
		XsqlBuilder builder = getXsqlBuilder();
		
		//混合使用otherFilters与pageRequest为一个filters使用
		XsqlFilterResult queryXsqlResult = builder.generateHql(hql,otherFilters,pageRequest);
		XsqlFilterResult countQueryXsqlResult = builder.generateHql(countQuery,otherFilters,pageRequest);
		
		return PageQueryUtils.pageQuery(getHibernateTemplate(),pageRequest,queryXsqlResult,countQueryXsqlResult);
	}
	
	protected XsqlBuilder getXsqlBuilder() {
		SessionFactoryImpl sf = (SessionFactoryImpl)(getSessionFactory());
		Dialect dialect = sf.getDialect();
		
		//or SafeSqlProcesserFactory.getMysql();
		SafeSqlProcesser safeSqlProcesser = SafeSqlProcesserFactory.getFromCacheByHibernateDialect(dialect); 
		XsqlBuilder builder = new XsqlBuilder(safeSqlProcesser);
		
		if(builder.getSafeSqlProcesser().getClass() == DirectReturnSafeSqlProcesser.class) {
			System.err.println(BaseHibernateDao.class.getSimpleName()+".getXsqlBuilder(): 故意报错,你未开启Sql安全过滤,单引号等转义字符在拼接sql时需要转义,不然会导致Sql注入攻击的安全问题，请修改源码使用new XsqlBuilder(SafeSqlProcesserFactory.getDataBaseName())开启安全过滤");
		}
		return builder;
	}
	
	static class PageQueryUtils {
		private static Page pageQuery(HibernateTemplate template,final PageRequest pageRequest, final XsqlFilterResult queryXsqlResult, final XsqlFilterResult countQueryXsqlResult) {
			return (Page)template.execute(new HibernateCallback() {
				public Object doInHibernate(Session session) throws HibernateException, SQLException {
					
					Query query = setQueryParameters(session.createQuery(queryXsqlResult.getXsql()),pageRequest);
					Query countQuery = setQueryParameters(session.createQuery(removeOrders(countQueryXsqlResult.getXsql())),pageRequest);
					
					return executeQueryForPage(pageRequest, query, countQuery);
				}
			});
		}
		
		private static Object executeQueryForPage(final PageRequest pageRequest,Query query, Query countQuery) {
			int totalCount = 0;
			if(countQuery.getQueryString().indexOf("group by") == -1){
				totalCount = Integer.parseInt(countQuery.uniqueResult().toString());
			}else{
				totalCount = countQuery.list().size();
			}
			Page page = new Page(pageRequest, totalCount);
			if(page.getTotalCount() <= 0) {
				page.setResult(new ArrayList(0));
			}else {
				page.setResult(query.setFirstResult(page.getFirstResult()).setMaxResults(page.getPageSize()).list());
			}
			return page;
		}
	
		public static Query setQueryParameters(Query q,Object params) {
			q.setProperties(params);
			return q;
		}
		
		public static Query setQueryParameters(Query q,Map params) {
			q.setProperties(params);
			return q;
		}
	}
	 
	public void save(E entity) {
		getHibernateTemplate().save(entity);
	}

	public List<E> findAll() {
		return getHibernateTemplate().loadAll(getEntityClass());
	}

	public E getById(PK id) {
		return (E)getHibernateTemplate().get(getEntityClass(),id);
	}

	public void delete(Object entity) {
		getHibernateTemplate().delete(entity);
	}
	
	public void delete(Serializable entity) {
		getHibernateTemplate().delete(entity);
	}
	
	public void deleteById(PK id) {
		Object entity = getById(id);
		if(entity == null) {
			throw new ObjectRetrievalFailureException(getEntityClass(),id);
		}
		getHibernateTemplate().delete(entity);
	}

	public void update(Object entity) {
		getHibernateTemplate().update(entity);
	}

	public void saveOrUpdate(E entity) {
		getHibernateTemplate().saveOrUpdate(entity);
	}

	public void refresh(Object entity) {
		getHibernateTemplate().refresh(entity);
	}

	public void flush() {
		getHibernateTemplate().flush();
	}

	public void evict(Object entity) {
		getHibernateTemplate().evict(entity);
	}

	public void saveAll(Collection<E> entities) {
		for(Iterator<E> it = entities.iterator(); it.hasNext();) {
			save(it.next());
		}
	}

	public void deleteAll(Collection entities) {
		getHibernateTemplate().deleteAll(entities);
	}

    public E findByProperty(final String propertyName, final Object value){
    	
        return (E)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return session.createCriteria(getEntityClass())
					.add(Expression.eq(propertyName,value))
					.uniqueResult();
			}
        });
    }

    public List<E> findAllBy(final String propertyName, final Object value) {
        return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				return session.createCriteria(getEntityClass())
					.add(Restrictions.eq(propertyName,value))
					.list();
			}
        });
    }

    /**
	 * 判断对象某些属性的值在数据库中是否唯一.
	 *
	 * @param uniquePropertyNames 在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
	 */
	public boolean isUnique(E entity, String uniquePropertyNames) {
		Assert.hasText(uniquePropertyNames);
		Criteria criteria = getSession().createCriteria(getEntityClass()).setProjection(Projections.rowCount());
		String[] nameList = uniquePropertyNames.split(",");
		try {
			// 循环加入唯一列
			for (int i = 0; i < nameList.length; i++) {
				criteria.add(Restrictions.eq(nameList[i], PropertyUtils.getProperty(entity, nameList[i])));
			}

			// 以下代码为了如果是update的情况,排除entity自身.

			String idName = getSessionFactory().getClassMetadata(entity.getClass()).getIdentifierPropertyName();
			if(idName != null) {
				// 取得entity的主键值
				Serializable id =  (Serializable)PropertyUtils.getProperty(entity, idName);
	
				// 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
				if (id != null)
					criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
			}
		} catch (Exception e) {
			ReflectionUtils.handleReflectionException(e);
		}
		return ((Number) criteria.uniqueResult()).intValue() == 0;
	}
	
	public List findByHQL(String hql, ArrayList values) {
		log.debug("finding Basic instance with hql: " + hql);
		try {
			String queryString = hql;
			Query queryObject = getSession().createQuery(queryString);
			for (int i = 0; values!=null&&i < values.size(); i++) {
				queryObject.setParameter(i, values.get(i));
			}
			
			return queryObject.list();
		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}
	public void excute(String sql, List values) {
		log.debug("finding Basic instance with sql: " + sql);
		try {
			String queryString = sql;
			
			SQLQuery sqlQuery=getSession().createSQLQuery(sql);
			for (int i = 0; values!=null&&i < values.size(); i++) {
				sqlQuery.setParameter(i, values.get(i));
			}
		
			sqlQuery.executeUpdate();
		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}
	public List find(String sql, ArrayList values) {
		log.debug("finding Basic instance with sql: " + sql);
		try {
			String queryString = sql;
			
			SQLQuery sqlQuery=getSession().createSQLQuery(sql);
			for (int i = 0; values!=null&&i < values.size(); i++) {
				sqlQuery.setParameter(i, values.get(i));
			}
			
			return sqlQuery.list();
		} catch (RuntimeException re) {
			log.error("find by property name failed", re);
			throw re;
		}
	}
	
	
	
	
    public abstract Class getEntityClass();
    
    
    /** 张国栋 新增方法 开始*/
    
    /**
     * 根据给定的hql语句和参数值创建查询对象
     * @param hql hql语句
     * @param value 参数值
     * @return 查询对象
     * @author 张国栋
     */
    public Query createQuery(final String hql,final Object value){
		return (Query) getHibernateTemplate().execute(new HibernateCallback() { 
			public Object doInHibernate(Session session) 
			throws HibernateException {
				Query query = session.createQuery(hql);
				query.setParameter(0, value);
				return query;
			}
	  });
	}
    
    /**
     * 根据给定的hql语句和参数值创建查询对象
     * @param hql hql语句
     * @param value 参数值
     * @return 查询对象
     * @author 张国栋
     */
    public Query createQuery(final String hql,final List<Object> values){
		return (Query) getHibernateTemplate().execute(new HibernateCallback() { 
			public Object doInHibernate(Session session) 
			throws HibernateException {
				Query query = session.createQuery(hql);
				for(int i=0;i<values.size();i++)
					query.setParameter(i, values.get(i));
				return query;
			}
	  });
	}
    
    /**
     * 根据给定的hql语句和参数值执行查询，返回唯一值
     * @param hql hql语句
     * @param value 参数值
     * @return 张国栋
     */
    public Object findUniqueResult(final String hql, final Object value){
    	return this.createQuery(hql, value).uniqueResult();
    }
    
    public Object findUniqueResult(final String hql, final List<Object> values){
    	return this.createQuery(hql, values).uniqueResult();
    }

    public Object findUniqueResult(final String hql){
    	return getSession().createQuery(hql).uniqueResult();
    }
    
    public Object getById(Class clazz,PK id) throws DataAccessException{
    	return getHibernateTemplate().get(clazz, id);
    }
    
    public List findAll(String hql){
    	return getHibernateTemplate().find(hql);
    }
    
    public List findAll(String hql, Object value){
    	Query query = getSession().createQuery(hql);
    	query.setParameter(0, value);
    	return query.list();
    }
    
    /** 张国栋 新增方法 结束*/
	public List<E> findAllBy(final Map<String,Object> map) {
        try {
			return getHibernateTemplate().executeFind(new HibernateCallback() {
				public Object doInHibernate(Session session) {
					return session.createCriteria(getEntityClass())
					.add(Restrictions.allEq(map))
					.list();
				}
			});
		} catch (DataAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
    }
    
    /**
     * 创建Query对象，并设置参数值
     * @param hql		hql语句
     * @param params	封装了参数名称及参数值
     * @return			返回Query对象
     */
    public Query createQuery(final String hql,final HashMap<String, Object> params){
		return (Query) getHibernateTemplate().execute(new HibernateCallback() { 
			public Object doInHibernate(Session session) 
			throws HibernateException {
				Query query = session.createQuery(hql);
				String[] keys = query.getNamedParameters();
				
				if (keys != null && params != null) {
					for(String key:keys){
						if(!params.containsKey(key)){
							throw new RuntimeException("没有设置参数"+key+"的值");
						}
						/**
						 * 不同的参数值类型，设置参数的方式不同，
						 * 集合类、数组类使用query.setParameterList(name, vals);
						 * 非集合类使用query.setParameter(key,value);
						 * author: ZhangGuodong
						 */
						Object value=params.get(key);
						String cla = value.getClass().toString();
						if(cla.startsWith("class java.util.") && cla.endsWith("Set")){
							query.setParameterList(key, (Set) value);
						}else{
							query.setParameter(key,value);
						}
					}
				}
				return query;
			}
	  });
	}
    
    /**
     * 根据给定的Query对象和参数集合，设置参数值
     * @param query		query对象
     * @param params	封装了参数名称及参数值
     */
    public void setParameters(final Query query,final HashMap<String, Object> params){
		String[] keys = query.getNamedParameters();
		if (keys != null && params != null) {
			for(String key:keys){
				if(!params.containsKey(key)){
					throw new RuntimeException("没有设置参数"+key+"的值");
				}
				query.setParameter(key,params.get(key));
			}
		}
	}
    
    /**
     * 根据hql语句，参数集合，返回符合条件的数据
     * @param hql		hql语句
     * @param params	封装了参数名称及参数值
     * @return			返回符合条件的数据集合
     */
    public List findList(final String hql, final HashMap<String, Object> params){
		return this.createQuery(hql, params).list();
	}
    
    /**
     * 根据hql语句，参数集合，返回符合条件的数据
     * @param hql		hql语句
     * @param params	封装了参数名称及参数值
     * @return			返回符合条件的数据集合
     */
    public List findList(final StringBuffer hql, final HashMap<String, Object> params){
		return this.findList(hql.toString(), params);
	}
    
    /**
     * 查询第一页的数据
     */
    public List findPage(final StringBuffer hql, final HashMap<String, Object> params, int pageSize){
    	Query query = this.createQuery(hql.toString(), params);
    	query.setMaxResults(pageSize);
		query.setFirstResult(0);
		return query.list();
    }
    
    /**
     * 根据hql语句，和参数集合，查询唯一的结果
     * @param hql		hql语句
     * @param params	参数
     * @return			符合条件的唯一结果
     */
    public Object findUniqueResult(final StringBuffer hql,final HashMap<String, Object> params) {
		return this.createQuery(hql.toString(), params).uniqueResult();
	}
    
    /**
     * 根据hql语句，和参数集合，查询唯一的结果
     * @param hql		hql语句
     * @param params	参数
     * @return			符合条件的唯一结果
     */
    public Long findLongUniqueResult(final StringBuffer hql,final HashMap<String, Object> params) {
		return this.findLongUniqueResult(hql.toString(), params);
	}
    
    /**
     * 根据hql语句，和参数集合，查询唯一的结果
     * @param hql		hql语句
     * @param params	参数
     * @return			符合条件的唯一结果
     */
    public Long findLongUniqueResult(final String hql,final HashMap<String, Object> params) {
		Object result = this.createQuery(hql, params).uniqueResult();
		if(result != null)
			return Long.parseLong(result.toString());
		else
			return 0l;
	}
    
	public Long saveObj(Object entity) {
		return Long.parseLong(getHibernateTemplate().save(entity).toString());
	}
	
	/**
	 * 根据SQL语句执行查询
	 * @param sql sql语句
	 * @param params 参数(key+value)
	 * @return 符合条件的数据
	 * @author ZhangGuodong
	 */
	public List findListBySql(StringBuilder sql, HashMap<String, Object> params){
		Query query = getSession().createSQLQuery(sql.toString());
		this.setParameters(query, params);
		return query.list();
	}
	
	/**
	 * 根据SQL语句执行查询
	 * @param sql sql语句
	 * @param value 参数值
	 * @return 符合条件的数据
	 * @author ZhangGuodong
	 */
	public List findListBySql(StringBuilder sql, Object value){
		Query query = getSession().createSQLQuery(sql.toString());
		query.setParameter(0, value);
		return query.list();
	}
}
